using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using TicTacToe.Opponents;

namespace TicTacToe
{
    /// <summary>
    /// Maintains the state of the current game, and state between games.
    /// This is written for a player vs computer scenario and does not suport two player or two computer versions.
    /// </summary>
    public class GameController
    {
        private Board _currentBoard;
        private string _status;

        //Track who made the last move, which determines who goes first in subsequent games.
        private Players? _lastPlayer;
        private readonly OpponentCatalog _catalog = new OpponentCatalog();

        public event EventHandler<CellFilledEventArgs> CellFilled;
        public event EventHandler<EventArgs> NewGame;
        public event EventHandler<StatusChangedEventArgs> StatusChanged;

        public Symbol PlayerSymbol { get; private set; }
        public Symbol OpponentSymbol { get; private set; }

        public GameController()
        {
            CurrentOpponent = _catalog.Opponents.First();
            SetSymbols();
        }

        public bool Complete { get; private set; }
        public IOpponent CurrentOpponent { get; set; }

        public string Status
        {
            get { return _status; }
            private set
            {
                _status = value;
                if (StatusChanged != null)
                    StatusChanged(this, new StatusChangedEventArgs(_status));
            }
        }

        public List<IOpponent> AvailableOpponents
        {
            get { return _catalog.Opponents; }
        }

        private void SetSymbols()
        {
            //Whether the player is X or O is randomly assigned on each game.
            var heads = GameRules.CoinToss();
            PlayerSymbol = heads ? Symbol.Nought : Symbol.Cross;
            OpponentSymbol = heads ? Symbol.Cross : Symbol.Nought;
        }

        public void BeginGame()
        {
            _currentBoard = new Board();

            if (NewGame != null)
                NewGame(this, new EventArgs());

            Complete = false;
            Status = "Starting new game...";

            //X goes first on the first game.
            //Assume that if this isn't our first game, then the player who lost the last game goes first.
            if (_lastPlayer.HasValue)
            {
                if (_lastPlayer == Players.Player)
                    MakeOpponentMove();
            }
            else if (OpponentSymbol == Symbol.Cross)
            {
                MakeOpponentMove();
            }

            Status = "Waiting for player to move...";
        }

        /// <summary>
        /// Call this method to process a player move.
        /// </summary>
        /// <param name="location">A point holding the co-ordinates of the desired move.</param>
        public void MakePlayerMove(Point location)
        {
            MakeMove(location, Players.Player);

            if (!Complete)
                MakeOpponentMove();
            
            if (!Complete)
                Status = "Waiting for player to move...";
        }

        private void MakeOpponentMove()
        {
            Status = "Your opponent is currently thinking...";
            var location = CurrentOpponent.ChooseMove(_currentBoard);
            MakeMove(location, Players.Computer);
        }

        private void MakeMove(Point location, Players player)
        {
            if (Complete)
                throw new InvalidMoveException("The game is complete. Please start a new game.");

            if (_currentBoard.GetValue(location).HasValue)
                throw new InvalidMoveException("The selected cell has already been filled.");

            var symbol = player == Players.Player ? PlayerSymbol : OpponentSymbol;
            _currentBoard.SetValue(location, symbol);

            if (CellFilled != null)
                CellFilled(this, new CellFilledEventArgs(location, symbol));

            _lastPlayer = player;

            CheckForWin(player.ToString() + " wins!");

            if (!Complete)
                CheckForDraw();
        }

        private void CheckForWin(string winMessage)
        {
            if (_currentBoard.IsWon())
            {
                Status = winMessage;
                Complete = true;
            }
        }

        private void CheckForDraw()
        {
            if (_currentBoard.IsDrawn())
            {
                Status = "It's a draw!";
                Complete = true;
                
            }
        }
    }
}